
/*
 * Copyright (C) 2010 Bitglue. All rights reserved.
 *
 * $Id: package-info.java bself $
 */

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.springframework.osgi.web.deployer.eclipse;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;

import javax.servlet.ServletContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.tomcat.JarScanner;
import org.apache.tomcat.JarScannerCallback;

/**
 * EclipseJarScanner is the org.apache.tomcat.util.scan.StandardJarScanner
 * ripped from the tomcat 7 source - tweaked and reformatted to my liking.
 * @author Tomcat Folks
 */
public class EclipseJarScanner implements JarScanner {

    private static final String SKIP_JARS_PROPERTY =
      "tomcat.util.scan.DefaultJarScanner.jarsToSkip";
    private static final Set<String> DEFAULTJARSTOSKIP
      = new HashSet<String>();
    protected final Log log = LogFactory.getLog(getClass());
    private boolean scanClassPath = true;
    private boolean scanAllFiles;
    private boolean scanAllDirectories;

    public boolean isScanClassPath() {
        return scanClassPath;
    }

    public void setScanClassPath(boolean scanClassPath) {
        this.scanClassPath = scanClassPath;
    }

    public boolean isScanAllFiles() {
        return scanAllFiles;
    }

    public void setScanAllFiles(boolean scanAllFiles) {
        this.scanAllFiles = scanAllFiles;
    }

    public boolean isScanAllDirectories() {
        return scanAllDirectories;
    }

    public void setScanAllDirectories(boolean scanAllDirectories) {
        this.scanAllDirectories = scanAllDirectories;
    }

    @Override
    public void scan(ServletContext context, ClassLoader classloader,
      JarScannerCallback callback, Set<String> jarsToSkip) {
        Set<String> ignoredJars;
        if (jarsToSkip == null)
            ignoredJars = DEFAULTJARSTOSKIP;
        else
            ignoredJars = jarsToSkip;

        // Scan WEB-INF/lib
        Set<String> dirList = context.getResourcePaths("/WEB-INF/lib/");
        if (dirList != null) {
            Iterator<String> it = dirList.iterator();
            while (it.hasNext()) {
                String path = it.next();
                if (path.endsWith(".jar") && !ignoredJars.contains(
                  path.substring(path.lastIndexOf('/') + 1))) {
                    URL url = null;
                    try {
                        url = context.getResource(path);
                        process(callback, url);
                    } catch (IOException e) {
                        log.error(path, e);
                    }
                }
            }
        }

        // Scan the classpath
        if (scanClassPath) {
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            while (loader != null) {
                if (loader instanceof URLClassLoader) {
                    URL[] urls = ((URLClassLoader) loader).getURLs();
                    for (int i = 0; i < urls.length; i++) {
                        String jarName = getJarName(urls[i]);
                        if (!(ignoredJars.contains(jarName) || urls[i]
                          .toString().contains("/WEB-INF/lib/" + jarName))) {
                            try {
                                process(callback, urls[i]);
                            } catch (IOException ioe) {
                                log.error("process", ioe);
                            }
                        }
                    }
                }
                loader = loader.getParent();
            }
        }
    }

    private void process(JarScannerCallback callback, URL url)
      throws IOException {
        URLConnection conn = url.openConnection();
        if (conn instanceof JarURLConnection)
            callback.scan((JarURLConnection) conn);
        else {
            String urlStr = url.toString();
            if (urlStr.startsWith("file:") || urlStr.startsWith("jndi:")) {
                if (urlStr.endsWith(".jar")) {
                    URL jarURL = new URL("jar:" + urlStr + "!/");
                    callback.scan((JarURLConnection) jarURL.openConnection());
                } else {
                    File f;
                    try {
                        f = new File(url.toURI());
                        if (f.isFile() && scanAllFiles) {
                            // Treat this file as a JAR
                            URL jarURL = new URL("jar:" + urlStr + "!/");
                            callback.scan((JarURLConnection)
                              jarURL.openConnection());
                        } else if (f.isDirectory() && scanAllDirectories) {
                            File metainf = new File(f.getAbsoluteFile() +
                              File.separator + "META-INF");
                            if (metainf.isDirectory())
                                callback.scan(f);
                        }
                    } catch (URISyntaxException e) {
                        IOException ioe = new IOException();
                        ioe.initCause(e);
                        throw ioe;
                    }
                }
            }
        }
    }

    private String getJarName(URL url) {
        String name = null;
        String path = url.getPath();
        int end = path.indexOf(".jar");
        if (end != -1) {
            int start = path.lastIndexOf('/', end);
            name = path.substring(start + 1, end + 4);
        }
        return name;
    }

    static {
        String jarList = System.getProperty(SKIP_JARS_PROPERTY);
        if (jarList != null) {
            StringTokenizer tokenizer = new StringTokenizer(jarList, ",");
            while (tokenizer.hasMoreElements()) {
                DEFAULTJARSTOSKIP.add(tokenizer.nextToken());
            }
        }
    }

}
